﻿using UnityEngine.AddressableAssets;

namespace LP
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    using UnityEngine.U2D;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Runtime.CompilerServices;

    public sealed class ResMgr : AbsMgr
    {

        /// <summary>
        /// 加载Resources下资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resName"></param>
        /// <returns></returns>
        public T Load<T>(string resName) where T : UnityEngine.Object
        {
            return Resources.Load<T>(resName);
        }

        /// <summary>
        /// 异步加载Resources下资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resName"></param>
        /// <param name="cb"></param>
        public void LoadAsync<T>(string resName, Action<T> cb = null) where T : UnityEngine.Object
        {
            global::Global.CoroutineMgrIns.StartCoroutine(LoadAsync_(resName, cb), TokenSource.Token);
        }

        private IEnumerator LoadAsync_<T>(string resName, Action<T> cb = null) where T : UnityEngine.Object
        {
            var resHandler = Resources.LoadAsync<T>(resName);
            yield return resHandler;
            cb?.Invoke(resHandler.asset as T);
        }

        /// <summary>
        /// 异步Task加载资源
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<T> LoadAssetAsyncTask<T>(string key) where T : UnityEngine.Object
        {
            try
            {
                var loadHandler = Addressables.LoadAssetAsync<T>(key);
                await loadHandler.Task;
                return loadHandler.Result;
            }
            catch (System.Exception e)
            {
                throw new Exception(e.ToString());
            }
        }

        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="cb"></param>
        /// <returns></returns>
        public void LoadAssetAsync<T>(string key, Action<T> cb = null)
        {
            global::Global.CoroutineMgrIns.StartCoroutine(LoadAssetAsync_(key, cb), TokenSource.Token);
        }

        private IEnumerator LoadAssetAsync_<T>(string key, Action<T> cb = null)
        {
            var handle = Addressables.LoadAssetAsync<T>(key);
            if (cb != null)
            {
                handle.Completed += (re) => { cb?.Invoke(re.Result); };
            }

            // AddRefHandle(key, handle);
            yield return handle;
        }

        /// <summary>
        /// 异步加载场景
        /// </summary>
        /// <param name="key"></param>
        /// <param name="processCb"></param>
        /// <param name="cb"></param>
        /// <returns></returns>
        public void LoadSceneAsync(string key, Action<float> processCb = null, Action cb = null,
            LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true)
        {
            string name = SceneManager.GetActiveScene().name;
            string[] strs = key.Split('/');
            if (strs[strs.Length - 1].Split('.')[0] != name)
            {
                global::Global.CoroutineMgrIns.StartCoroutine(LoadSceneAsync_(key, processCb, cb, sceneMode, activateOnLoad),
                    TokenSource.Token);
            }
        }

        private IEnumerator LoadSceneAsync_(string key, Action<float> processCb = null, Action cb = null,
            LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true)
        {
            var handle = Addressables.LoadSceneAsync(key, sceneMode, activateOnLoad);
            if (cb != null)
            {
                handle.Completed += (re) => { cb?.Invoke(); };
            }

            // AddRefHandle(key, handle);
            while (!handle.IsDone)
            {
                processCb?.Invoke(handle.PercentComplete);
                yield return null;
            }
        }

        /// <summary>
        /// 异步去加载所有资源
        /// </summary>
        /// <param name="assetDic">需要加载的资源字典，资源路径|资源对象</param>
        /// <param name="finishCallback">完成后的回调</param>
        /// <param name="startIndex"></param>
        /// <typeparam name="T">资源类型</typeparam>
        public void AsyncLoadAllAsset<T>(Dictionary<string, T> assetDic, Action finishCallback, int startIndex = 0)
        {
            string key = assetDic.Keys.ToList()[startIndex];
            global::Global.ResMgrIns.LoadAssetAsync(key, delegate (T asset)
            {
                assetDic[key] = asset;
                startIndex++;
                if (assetDic.Count > startIndex)
                {
                    AsyncLoadAllAsset(assetDic, finishCallback, startIndex);
                }
                else
                {
                    finishCallback?.Invoke();
                }
            });
        }

        /// <summary>
        /// 异步去加载所有资源
        /// </summary>
        public void AsyncLoadAllAsset<T>(Dictionary<string, T> assetDic, Action finishCallback)
        {
            List<string> keyList = assetDic.Keys.ToList();
            int finishNum = 0;
            for (int i = 0; i < assetDic.Count; i++)
            {
                var i1 = i;
                LoadAssetAsync<T>(keyList[i], delegate (T obj)
                {
                    assetDic[keyList[i1]] = obj;
                    finishNum++;
                    if (finishNum == assetDic.Count)
                    {
                        finishCallback?.Invoke();
                    }
                });
            }
        }

        /// <summary>
        /// 异步实例化GameObject
        /// </summary>
        /// <param name="key"></param>
        /// <param name="position"></param>
        /// <param name="rotation"></param>
        /// <param name="parent"></param>
        /// <param name="cb"></param>
        /// <returns></returns>
        public void InstantiateAsync(object key, Transform parent = null, Action<GameObject> cb = null)
        {
            global::Global.CoroutineMgrIns.StartCoroutine(InstantiateAsync_(key, parent, cb), TokenSource.Token);
        }

        private IEnumerator InstantiateAsync_(object key, Transform parent = null, Action<GameObject> cb = null)
        {
            var handle =
                Addressables.InstantiateAsync(key, Vector3.zero, Quaternion.identity, parent, true);
            if (cb != null)
            {
                handle.Completed += (re) => { cb?.Invoke(re.Result); };
            }

            yield return handle;
        }

        public async Task<T> InstantiateAsyncTask<T>(string key, Transform parent = null) where T : UnityEngine.Object
        {
            try
            {
                var handle =
                    Addressables.InstantiateAsync(key, Vector3.zero, Quaternion.identity, parent, true);
                await handle.Task;
                var ret = handle.Result as T;
                return ret;
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.ToString());
                return null;
            }
        }

        /// <summary>
        /// 异步加载精灵图集中单张精灵
        /// </summary>
        /// <param name="key">MySpriteSheetAddress[MySpriteName]</param>
        /// <param name="cb"></param>
        /// <returns></returns>
        public void LoadSprite(string atalsName, string key, Action<Sprite> cb = null)
        {
            global::Global.CoroutineMgrIns.StartCoroutine(LoadSprite_(atalsName, key, cb), TokenSource.Token);
        }

        private IEnumerator LoadSprite_(string atalsName, string key, Action<Sprite> cb = null)
        {
            yield return LoadAssetAsync_<SpriteAtlas>(atalsName, (atals) =>
            {
                var sprite = atals.GetSprite(key);
                cb?.Invoke(sprite);
            });
            ;
        }

        /// <summary>
        /// 异步加载精灵图集表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="cb"></param>
        /// <returns></returns>
        public void LoadSpriteSheet(string key, Action<IList<Sprite>> cb = null)
        {
            global::Global.CoroutineMgrIns.StartCoroutine(LoadSpriteSheet_(key, cb), TokenSource.Token);
        }

        private IEnumerator LoadSpriteSheet_(string key, Action<IList<Sprite>> cb = null)
        {
            yield return LoadAssetAsync_<SpriteAtlas>(key, (atals) =>
            {
                Sprite[] sprites = new Sprite[atals.spriteCount];
                atals.GetSprites(sprites);
                cb?.Invoke(sprites.ToList<Sprite>());
            });
            ;
        }

        /// <summary>
        /// 强制卸载所有未使用的assets
        /// </summary>
        public void ForceUnloadAssets()
        {
            Resources.UnloadUnusedAssets();
        }
    }
}